#include <string.h>
#include <stdio.h>
#include "naskfunc.h"
#include "graphic.h"
#include "dsctbl.h"
#include "int.h"
#include "fifo.h"
#include "keyboard.h"
#include "memory.h"
#include "sheet.h"
#include "timer.h"
#include "mtask.h"
#include "file.h"
#include "console.h"
#include "window.h"

extern struct TASKCTL *taskctl;

void HariMain(void) {
	// 取asmhead.nas中存储的值
	struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO;
	struct SHTCTL *shtctl;
	char sbuf[40];
	struct FIFO32 fifo, keycmd;
	int fifobuf[128], keycmd_buf[32];
	int mx, my, i, new_mx = -1, new_my = 0, new_wx = 0x7fffffff, new_wy = 0;
	unsigned int memtotal;
	struct MOUSE_DEC mdec;
	struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDRESS;
	unsigned char *buf_back, buf_mouse[256];
	struct SHEET *sht_back, *sht_mouse;
	struct TASK *task_a, *task;
	static char keytable0[0x80] = { //
			0, 0, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', 0x08, 0, //
					'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '[', ']', 0x0a, 0, //
					'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ';', '\'', 0, 0, '\\', //
					'Z', 'X', 'C', 'V', 'B', 'N', 'M', ',', '.', '/', 0, '*', 0, ' ', 0, 0, 0, 0, 0, 0, //
					0, 0, 0, 0, 0, 0, 0, '7', '8', '9', '-', '4', '5', '6', '+', '1', //
					'2', '3', '0', '.', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
					0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
					0, 0, 0, 0x5c, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x5c, 0, 0 //
			};
	static char keytable1[0x80] = { //
			0, 0, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', 0x08, 0, //
					'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', 0x0a, 0, //
					'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', 0, 0, '|', //
					'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', 0, '*', 0, ' ', 0, 0, 0, 0, 0, 0, //
					0, 0, 0, 0, 0, 0, 0, '7', '8', '9', '-', '4', '5', '6', '+', '1', //
					'2', '3', '0', '.', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
					0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
					0, 0, 0, '_', 0, 0, 0, 0, 0, 0, 0, 0, 0, '|', 0, 0 //
			};
	int key_shift = 0, key_leds = (binfo->leds >> 4) & 7, keycmd_wait = -1;
	int j, x, y;
	struct SHEET *sht = 0, *key_win, *sht2;
	int mmx = -1, mmy = -1, mmx2 = 0; //鼠标移动的距离
	int *fat;
	unsigned char *language;
	struct FILEINFO *finfo;
	extern char hankaku[4096]; // 英文字符

	init_gdtidt(); /* 初始化GDT, IDT */
	init_pic(); /* 初始化PIC */
	io_sti(); /* 打开所有可屏蔽中断 */
	fifo32_init(&fifo, 128, fifobuf, 0);
	init_pit();

	init_keyboard(&fifo, 256);/* 初始化键盘缓冲区结构体 */
	enable_mouse(&fifo, 512, &mdec);/* 初始化鼠标缓冲区结构体 */

	io_out8(PIC0_IMR, 0xf8); /* PIC0(11111001) (打开IRQ1键盘中断和连接从PIC的IRQ2)*/
	io_out8(PIC1_IMR, 0xef); /* PIC1(11101111) (打开PS2鼠标中断 即IRQ12)*/
	fifo32_init(&keycmd, 32, keycmd_buf, 0);

	memtotal = memtest(0x00400000, 0xbfffffff);
	memman_init(memman);
	memman_free(memman, 0x00001000, 0x0009e000);
	memman_free(memman, 0x00400000, memtotal - 0x00400000);

	init_palette();/* 初始化调色板 */
	shtctl = shtctl_init(memman, binfo->vram, binfo->scrnx, binfo->scrny);
	task_a = task_init(memman);
	task_a->langmode = 0;
	fifo.task = task_a; // 负责鼠标移动，键盘输入数据的分配
	task_run(task_a, 1, 2);
	*((int *) 0x0fe4) = (int) shtctl; //保存值在console中0x5程序号使用
	*((int *) 0x0fec) = (int) &fifo;

	// sht_back 背景
	sht_back = sheet_alloc(shtctl);
	buf_back = (unsigned char*) memman_alloc_4k(memman, binfo->scrnx * binfo->scrny);
	sheet_setbuf(sht_back, buf_back, binfo->scrnx, binfo->scrny, -1);
	init_screen8(buf_back, binfo->scrnx, binfo->scrny);

	// sht_cons
	key_win = open_console(shtctl, memtotal);

	// sht_mouse
	sht_mouse = sheet_alloc(shtctl);
	sheet_setbuf(sht_mouse, buf_mouse, 16, 16, 99);
	init_mouse_cursor8(buf_mouse, 99);
	mx = (binfo->scrnx - 16) / 2;
	my = (binfo->scrny - 28 - 16) / 2;

	sheet_slide(sht_back, 0, 0);
	sheet_slide(key_win, 32, 4);
	sheet_slide(sht_mouse, mx, my);
	sheet_updown(sht_back, 0);
	sheet_updown(key_win, 1);
	sheet_updown(sht_mouse, 2);

	keywin_on(key_win);

	sprintf(sbuf, "(%3d, %3d)", mx, my);
	putfonts8_asc_sht(sht_back, 0, 0, COL8_FFFFFF, COL8_008484, sbuf, 10);
	sprintf(sbuf, "memory %dMB free: %dKB", memtotal / (1024 * 1024), memman_total(memman) / 1024);
	putfonts8_asc_sht(sht_back, 0, 32, COL8_FFFFFF, COL8_008484, sbuf, 40);

	// 为避免和键盘当前状态冲突，在一开始先进行设置
	fifo32_put(&keycmd, KEYCMD_LED);
	fifo32_put(&keycmd, key_leds);

	// 加载字库文件
	language = (unsigned char *) memman_alloc_4k(memman, 16 * 256 + 32 * 94 * 47);
	fat = (int *) memman_alloc_4k(memman, 4 * 2880);
	file_readfat(fat, (unsigned char *) (ADR_DISKIMG + 0x000200));
	finfo = file_search("nihongo.fnt", (struct FILEINFO *) (ADR_DISKIMG + 0x002600), 224);
	if (finfo != 0) {
		file_load(finfo->clustno, finfo->size, language, fat, (char *) (ADR_DISKIMG + 0x003e00));
	} else {
		for (i = 0; i < 16 * 256; i++) {
			language[i] = hankaku[i];
		}
		for (i = (16 * 256); i < (16 * 256 + 32 * 94 * 47); i++) {
			language[i] = 0xff;
		}
	}
	*((int *) 0x0fe8) = (int) language;
	memman_free_4k(memman, (int) fat, 4 * 2880);

	for (;;) {
		if (fifo32_status(&keycmd) > 0 && keycmd_wait < 0) {
			// 如果存在向键盘控制器发送的数据，则发送它
			keycmd_wait = fifo32_get(&keycmd);
			wait_KBC_sendready();
			io_out8(PORT_KEYDAT, keycmd_wait);
		}

		io_cli();
		if (fifo32_status(&fifo) == 0) {
			// FIFO为空，当存在搁置的绘图操作时立即执行
			if (new_mx >= 0) {
				io_sti();
				sheet_slide(sht_mouse, new_mx, new_my);
				new_mx = -1;
			} else if (new_wx != 0x7fffffff) {
				io_sti();
				sheet_slide(sht, new_wx, new_wy);
				new_wx = 0x7fffffff;
			} else {
				task_sleep(task_a);
				io_sti();
			}
		} else {
			i = fifo32_get(&fifo);
			io_sti();
			if (key_win != 0 && key_win->flags == TASK_INIT) { // 窗口被关闭
				if (shtctl->top == 1) { //当画面上只剩下鼠标很背景
					key_win = 0;
				} else {
					key_win = shtctl->sheets[shtctl->top - 1];
					keywin_on(key_win);
				}
			}
			if (256 <= i && i <= 511) {/* 如果键盘缓冲区中有数据 */
				sprintf(sbuf, "%02X", i - 256);
				putfonts8_asc_sht(sht_back, 0, 16, COL8_FFFFFF, COL8_008484, sbuf, 2);
				if (i < 0x80 + 256) {
					if (key_shift == 0) {
						sbuf[0] = keytable0[i - 256];
					} else {
						sbuf[0] = keytable1[i - 256];
					}
				} else {
					sbuf[0] = 0;
				}

				if ('A' <= sbuf[0] && sbuf[0] <= 'Z') {
					if (((key_leds & 4) == 0 && key_shift == 0) || ((key_leds & 4) != 0 && key_shift != 0)) { //(CapsLock=OFF&Shift=OFF)||(CapsLock=NO&Shift=NO)
						sbuf[0] += 0x20; // 大写字母转换成小写字符
					}
				}
				if (sbuf[0] != 0 && key_win != 0) { //一般字符、退格键和回车键
					fifo32_put(key_win->task->fifo, sbuf[0] + 256);
				}
				if (i == 256 + 0x0f && key_win != 0) { // tab
					keywin_off(key_win);
					j = key_win->height - 1;
					if (j == 0) {
						j = shtctl->top - 1;
					}
					key_win = shtctl->sheets[j];
					keywin_on(key_win);
				}
				if (i == 256 + 0x2a) { // 左shift ON
					key_shift |= 1;
				}
				if (i == 256 + 0x36) { // 右shift ON
					key_shift |= 2;
				}
				if (i == 256 + 0xaa) { // 左shift OFF
					key_shift &= ~1;
				}
				if (i == 256 + 0xb6) { // 右shift OFF
					key_shift &= ~2;
				}
				if (i == 256 + 0x3a) { // CapsLock
					key_leds ^= 4;
					fifo32_put(&keycmd, KEYCMD_LED);
					fifo32_put(&keycmd, key_leds);
				}
				if (i == 256 + 0x45) { // NumLock
					key_leds ^= 2;
					fifo32_put(&keycmd, KEYCMD_LED);
					fifo32_put(&keycmd, key_leds);
				}
				if (i == 256 + 0x46) { // ScrollLock
					key_leds ^= 1;
					fifo32_put(&keycmd, KEYCMD_LED);
					fifo32_put(&keycmd, key_leds);
				}
				if (i == (256 + 0x3b) && key_shift != 0 && key_win != 0) { // Shift+F1
					task = key_win->task;
					if (task != 0 && task->tss.ss0 != 0) {
						cons_putstr(task->cons, "\nBreak(key) :\n");
						io_cli();
						task->tss.eax = (int) &(task->tss.esp0);
						task->tss.eip = (int) asm_end_app;
						io_sti();
						task_run(task, -1, 0);
					}
				}
				if (i == (256 + 0x3c) && key_shift != 0) { // Shift + F2
					if (key_win != 0) {
						keywin_off(key_win);
					}
					key_win = open_console(shtctl, memtotal);
					sheet_slide(key_win, 32, 4);
					sheet_updown(key_win, shtctl->top);
					keywin_on(key_win);
				}
				if (i == 256 + 0x57) { // F11
				// F11 shtctl->sheets[0]是桌面背景,shtctl->top的高度是鼠标的高度
					sheet_updown(shtctl->sheets[1], shtctl->top - 1);
				}
				if (i == 256 + 0xfa) { // 键盘成功接收到数据
					keycmd_wait = -1;
				}
				if (i == 256 + 0xfe) { // 键盘没有成功接收到数据
					wait_KBC_sendready();
					io_out8(PORT_KEYDAT, keycmd_wait); //从新发送之前的数据
				}
			} else if (512 <= i && i <= 767) {
				// 鼠标共有三个字节，接收到鼠标要发送数据的状态0xfa后，读三个字节
				if (mouse_decode(&mdec, i - 512) != 0) {
					sprintf(sbuf, "[lcr %4d %4d]", mdec.x, mdec.y);
					if ((mdec.btn & 0x01) != 0) {/* 如果左键被按下 */
						sbuf[1] = 'L';
					}
					if ((mdec.btn & 0x02) != 0) {/* 如果右键被按下 */
						sbuf[3] = 'R';
					}
					if ((mdec.btn & 0x04) != 0) {/* 如果滚轮被按下 */
						sbuf[2] = 'C';
					}
					putfonts8_asc_sht(sht_back, 32, 16, COL8_FFFFFF, COL8_008484, sbuf, 15);
					mx += mdec.x;
					my += mdec.y;
					if (mx < 0) {
						mx = 0;
					}
					if (my < 0) {
						my = 0;
					}
					if (mx > binfo->scrnx - 1) {
						mx = binfo->scrnx - 1;
					}
					if (my > binfo->scrny - 1) {
						my = binfo->scrny - 1;
					}
					sprintf(sbuf, "(%3d, %3d)", mx, my);
					putfonts8_asc_sht(sht_back, 0, 0, COL8_FFFFFF, COL8_008484, sbuf, 10);
					new_mx = mx;
					new_my = my;
					if ((mdec.btn & 0x01) != 0) { // 按下右键
						//sheet_slide(sht_win, mx - 80, my - 8);
						if (mmx < 0) {
							// 如果处于普通模式，按照从上到下的顺序寻找鼠标所指向的图层
							for (j = shtctl->top - 1; j > 0; j--) {
								sht = shtctl->sheets[j];
								// 判定鼠标在画面中的位置,是否在窗体标题栏上，如在说明窗体是可以移动的
								x = mx - sht->vx0;
								y = my - sht->vy0;
								if (0 <= x && x < sht->bxsize && 0 <= y && y < sht->bysize) {
									if (sht->buf[y * sht->bxsize + x] != sht->col_inv) {
										sheet_updown(sht, shtctl->top - 1);
										// 自动切换输入状态
										if (sht != key_win) {
											keywin_off(key_win);
											key_win = sht;
											keywin_on(key_win);
										}
										if (3 <= x && x < sht->bxsize - 3 && 3 <= y && y < 21) {
											// 窗体移动模式
											mmx = mx;
											mmy = my;
											mmx2 = sht->vx0;
											new_wy = sht->vy0;
										}
										if (sht->bxsize - 21 <= x && x < sht->bxsize - 5 && 5 <= y && y < 19) {
											if ((sht->flags & 0x10) != 0) { //是否是由应用程序生成的窗体
												// 点击'X'
												task = sht->task;
												cons_putstr(task->cons, "\n Break(mouse) :\n");
												io_cli();
												task->tss.eax = (int) &(task->tss.esp0);
												task->tss.eip = (int) asm_end_app;
												io_sti();
												task_run(task, -1, 0);
											} else { // 向命令行发送数据，关闭命令行
												task = sht->task;
												sheet_updown(sht, -1);
												keywin_off(key_win);
												key_win = shtctl->sheets[shtctl->top - 1];
												keywin_on(key_win);
												io_cli();
												fifo32_put(task->fifo, 4);
												io_sti();
											}
										}
										break;
									}
								}
							}
						} else {
							// 处于移动模式
							x = mx - mmx;
							y = my - mmy;
							new_wx = (mmx2 + x + 2) & ~3;
							new_wy = new_wy + y;
							mmy = my;
						}
					} else {
						// 没有按下左键
						mmx = -1; // 返回普通模式
						if (new_wx != 0x7fffffff) {
							sheet_slide(sht, new_wx, new_wy);
							new_wx = 0x7fffffff;
						}
					}
				}
			} else if (768 <= i && i <= 1023) {
				// cons->sht - shtctl->sheets0 + 768 = i
				// cons->sht  = shtctl->sheets0 + (i - 768)
				close_console(shtctl->sheets0 + (i - 768));
			} else if (1024 <= i && i <= 2023) {
				close_constask(taskctl->tasks0 + (i - 1024));
			} else if (2024 <= i && i <= 2279) {
				sht2 = shtctl->sheets0 + (i - 2024);
				memman_free_4k(memman, (int) sht2->buf, 256 * 165);
				sheet_free(sht2);
			}
		}
	}
}

